package Watch

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
)

var comment byte

/**
 * @api {myInit(sysType string)} 路径 标题
 * @apiDescription 根据输入string判断所用系统类型
 *
 * @apiParam {string} sysType 系统类型
 */
func myInit(sysType string) {
	if sysType == "Linux" {
		comment = '#'
	} else if sysType == "Windows" {
		comment = ';'
	}
}

// 函数签名
type ListenFunc func(string)

// 自定义接口
type Listener interface {
	listen(infile string)
}

//函数签名ListenFunc实现接口方法
func (Listen ListenFunc) listen(inifile string) {
	// 调用具体的fileListen
	fileListen(inifile)
}

/**
 * @api {fileListen(fileName string)} 路径 标题
 * @apiDescription 监听文件内容是否被修改
 *
 * @apiParam {string} fileName 文件名称（路径）
 */
func fileListen(fileName string) {
	originSlice := []string{}
	originSlice = fileContent(fileName)
	flag := false

	for {
		curSlice := []string{}
		curSlice = fileContent(fileName)

		for i, value := range originSlice {
			if value != curSlice[i] {
				flag = true
				break
			}
		}

		if flag == true {
			break
		}
	}
}

/**
 * @api {fileContent(fileName string) []string} 路径 标题
 * @apiDescription 获取文件内容保存在切片并返回
 *
 * @apiParam {string} fileName 文件名称（路径）
 */
func fileContent(fileName string) []string {
	// 声明了一个暂不确定长度大小的string切片
	// 一个切片在未初始化之前默认为 nil，长度为 0
	var strSlice []string
	file, err := os.Open(fileName)
	// 打开文件错误
	if err != nil {
		return strSlice
	}
	// 函数结束时关闭文件
	defer file.Close()
	// 读取文件内容
	reader := bufio.NewReader(file)

	for {
		// 读取一行文件
		linestr, err := reader.ReadString('\n')
		strSlice = append(strSlice, linestr)
		// 错误判断
		if err != nil {
			break
		}

	}

	return strSlice
}

// 自定义错误类型
func createErr(text string) errorString {
	return errorString{text}
}

// 错误信息的数据结构
type errorString struct {
	str string
}

// 错误的方法
func (err *errorString) Error() string {
	fmt.Fprintf(os.Stdout, err.str)
	return err.str
}

/**
 * @api {readFile(filename string) (map[string]string, errorString)} 路径 标题
 * @apiDescription 读取文件并返回ini的键值对和自定义错误类型
 *
 * @apiParam {string} filename 文件名称（路径）
 */
func readFile(filename string) (map[string]string, errorString) {
	// 打开文件
	var error errorString
	file, err := os.Open(filename)
	configuration := make(map[string]string)
	// 打开文件错误
	if err != nil {
		error = createErr("error1: failed to open the configuration file")
		return configuration, error
	} else {
		error = createErr("")
	}

	// 函数结束时关闭文件
	defer file.Close()
	// 读取文件内容
	reader := bufio.NewReader(file)
	for {
		// 读取一行文件
		linestr, err := reader.ReadString('\n')
		// 最后一行没有换行符，err==EOF直接退出， 不能读取最后一行
		if err != nil && err != io.EOF {
			fmt.Println(err)
			break
		}

		// 切掉行的左右两边的空白字符
		linestr = strings.TrimSpace(linestr)
		// 忽略空行、注释、分段
		if linestr == "" || linestr[0] == comment || linestr[0] == '[' {
			continue
		}
		pair := strings.Split(linestr, "=")
		// 保证切开只有一个等号的情况
		if len(pair) == 2 {
			// 去掉多余空白字符
			key := strings.TrimSpace(pair[0])
			value := strings.TrimSpace(pair[1])
			configuration[key] = value
			// fmt.Println(key)
		}

		// 判断是否为最后一行，可改为：err ！= io.EOF
		if err != nil {
			// fmt.Println(err)
			break
		}
	}

	return configuration, error
}

/**
 * @api {Watch(filename string, Listen Listener) (map[string]string, errorString)} 路径 标题
 * @apiDescription 监听文件，如果文件内容被修改则输出修改前后的文件内容
 *
 * @apiParam {string} filename 文件名称（路径）
 * @apiParam {Listener} Listen 自定义监听器
 */
func Watch(filename string, Listen Listener) (map[string]string, errorString) {
	myInit("Windows")
	// 第一次读取文件
	config, error1 := readFile(filename)
	if len(error1.str) != 0 {
		fmt.Printf("First - Error occurs : %s", error1)
		return config, error1
	}

	// 打印配置文件信息
	printConfig(config)

	// 打印原始文件内容
	printFile(filename)
	fmt.Printf("Listening---------------------------------------------------:\n")
	// 开启监听文件
	Listen.listen(filename)
	// 文件被修改
	fmt.Printf("The ini file has been changed :\n")
	// 打印修改后文件内容
	printFile(filename)

	return config, error1
}

/**
 * @api {printConfig(configure map[string]string)} 路径 标题
 * @apiDescription 输出ini配置文件的键值对
 *
 * @apiParam {map[string]string} configure 保存键值对的map
 */
func printConfig(configure map[string]string) {
	fmt.Printf("Print configure begin: \n")
	for key, value := range configure {
		fmt.Println(key, "=", value)
	}
	fmt.Printf("Print configure end\n\n")
}

/**
 * @api {printFile(fileName string)} 路径 标题
 * @apiDescription 输出指定文件内容
 *
 * @apiParam {string} fileName 文件名称（路径）
 */
func printFile(fileName string) {
	// 打开文件
	file, err := os.Open(fileName)
	// 打开文件错误
	if err != nil {
		return
	}

	// 函数结束时关闭文件
	defer file.Close()
	// 读取文件内容
	reader := bufio.NewReader(file)
	fmt.Println("Print File begin")
	for {
		// 读取一行文件
		linestr, err := reader.ReadString('\n')
		fmt.Println(linestr)
		if err != nil {
			break
		}
	}
	fmt.Println("Print File end")
}
